#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <random>
#include <atomic>
#include <queue>

using namespace std::chrono_literals;

struct Packet {
    int seqNum;
    std::string data;
};

struct Ack {
    int ackNum;
};


std::queue<Packet> channel_A_to_B;
std::queue<Ack> channel_B_to_A;
std::mutex channel_mutex;
std::condition_variable cv_A_to_B;
std::condition_variable cv_B_to_A;

std::atomic<bool> ack_received(false);
std::atomic<int> last_ack_num(-1);
std::atomic<bool> stop_simulation(false);

const double PACKET_LOSS_PROBABILITY = 0.2;
const double ACK_LOSS_PROBABILITY = 0.1;
const auto TIMEOUT_DURATION = 150ms;


std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<double> distribution(0.0, 1.0);

void sender() {
    int next_seq_num = 0;
    int packet_count = 0;
    const int max_packets = 10;

    while (packet_count < max_packets && !stop_simulation) {
        Packet packet;
        packet.seqNum = next_seq_num;
        packet.data = "Packet " + std::to_string(packet_count);

        ack_received = false;
        last_ack_num = -1;

        while (!ack_received && !stop_simulation) {
            std::cout << "[Sender] Sending " << packet.data << " with seq=" << packet.seqNum << std::endl;

            {
                std::lock_guard<std::mutex> lock(channel_mutex);
                if (distribution(generator) >= PACKET_LOSS_PROBABILITY) {
                    channel_A_to_B.push(packet);
                } else {
                     std::cout << "[Network] >>> Packet " << packet.seqNum << " lost A->B <<<" << std::endl;
                }
            }
            cv_A_to_B.notify_one();


            auto start_time = std::chrono::steady_clock::now();
            bool timed_out = false;
            while(std::chrono::steady_clock::now() < start_time + TIMEOUT_DURATION){
                 if(ack_received){
                     break;
                 }
                 std::this_thread::sleep_for(10ms);
            }

            if (ack_received && last_ack_num == next_seq_num) {
                 std::cout << "[Sender] Received ACK for seq=" << next_seq_num << std::endl;
                 next_seq_num = 1 - next_seq_num;
                 packet_count++;
                 break;
            } else if (!ack_received) {
                 std::cout << "[Sender] Timeout for seq=" << packet.seqNum << ". Resending." << std::endl;
                 timed_out = true;
            } else {
                 std::cout << "[Sender] Received wrong ACK (" << last_ack_num << "), expected " << next_seq_num << ". Ignoring." << std::endl;
            }
        }
         std::this_thread::sleep_for(50ms);
    }
     std::cout << "[Sender] Finished sending packets." << std::endl;
     stop_simulation = true;
     cv_A_to_B.notify_one();
     cv_B_to_A.notify_one();
}

void receiver() {
    int expected_seq_num = 0;

    while (!stop_simulation) {
        Packet received_packet;
        bool packet_available = false;
        {
            std::unique_lock<std::mutex> lock(channel_mutex);
            cv_A_to_B.wait(lock, [&]{ return !channel_A_to_B.empty() || stop_simulation; });

            if (stop_simulation && channel_A_to_B.empty()) break;
            if (!channel_A_to_B.empty()){
                received_packet = channel_A_to_B.front();
                channel_A_to_B.pop();
                packet_available = true;
            }
        }


        if (packet_available) {
             std::cout << "[Receiver] Received packet with seq=" << received_packet.seqNum << std::endl;

            if (received_packet.seqNum == expected_seq_num) {
                 std::cout << "[Receiver] Packet seq=" << expected_seq_num << " is expected. Processing." << std::endl;
                 std::cout << "[Receiver] Data: " << received_packet.data << std::endl;

                 Ack ack;
                 ack.ackNum = expected_seq_num;
                 expected_seq_num = 1 - expected_seq_num;

                {
                    std::lock_guard<std::mutex> lock(channel_mutex);
                    if (distribution(generator) >= ACK_LOSS_PROBABILITY) {
                         std::cout << "[Receiver] Sending ACK for seq=" << ack.ackNum << std::endl;
                         channel_B_to_A.push(ack);
                    } else {
                         std::cout << "[Network] <<< ACK " << ack.ackNum << " lost B->A >>>" << std::endl;
                    }
                }
                cv_B_to_A.notify_one();

            } else {
                 std::cout << "[Receiver] Packet seq=" << received_packet.seqNum << " is unexpected (expected " << expected_seq_num << "). Discarding." << std::endl;

                 Ack ack;
                 ack.ackNum = 1 - expected_seq_num;
                {
                     std::lock_guard<std::mutex> lock(channel_mutex);
                     if (distribution(generator) >= ACK_LOSS_PROBABILITY) {
                         std::cout << "[Receiver] Resending ACK for last received seq=" << ack.ackNum << std::endl;
                         channel_B_to_A.push(ack);
                     } else {
                          std::cout << "[Network] <<< ACK " << ack.ackNum << " lost B->A >>>" << std::endl;
                     }
                 }
                cv_B_to_A.notify_one();
            }
        }

         Ack received_ack;
         bool ack_available = false;
         {
            std::unique_lock<std::mutex> lock(channel_mutex);
            if (!channel_B_to_A.empty()) {
                 received_ack = channel_B_to_A.front();
                 channel_B_to_A.pop();
                 ack_available = true;
            }
         }
         if (ack_available) {
             last_ack_num = received_ack.ackNum;
             ack_received = true;
         }


         std::this_thread::sleep_for(10ms);
    }
     std::cout << "[Receiver] Simulation stopped." << std::endl;
}


int main_stop_wait(int argc, char* argv[]) {
    std::thread sender_thread(sender);
    std::thread receiver_thread(receiver);

    sender_thread.join();
    receiver_thread.join();

    std::cout << "Stop-and-Wait simulation finished." << std::endl;
    return 0;
}

// --- Main selector ---
// Uncomment the main function you want to run
/*
int main(int argc, char* argv[]) {
     // return main_ntp(argc, argv);
     return main_stop_wait(argc, argv);
    // return main_sliding_window(argc, argv);
}
*/